package com.frank.loadinglibrary.common.element;

import com.frank.loadinglibrary.base.baseanimations.BaseAnimationController;
import com.frank.loadinglibrary.base.baseelement.BaseElement;
import com.frank.loadinglibrary.base.baseelement.IComponentCallback;
import com.frank.loadinglibrary.utils.DensityUtil;
import com.frank.loadinglibrary.utils.SimpleAnimatorListener;
import ohos.agp.animation.Animator;
import ohos.agp.animation.AnimatorValue;
import ohos.agp.render.Canvas;
import ohos.agp.render.Paint;
import ohos.agp.render.Path;
import ohos.agp.utils.Color;

import java.util.ArrayList;

/**
 * @author SUQI
 * @date 2021/6/4
 * @description
 **/
public class BallDancerElement extends BaseElement implements BaseAnimationController.IAnimationController<AnimatorValue> {

    //默认值
    private final float valueZeroPointFive = 0.5f;
    private static float RADIUS;
    private static float GAP;
    private static final float RTL_SCALE = 0.7f;
    private static final float LTR_SCALR = 1.3f;
    private static final int DURATION = 350;
    private static final int PAUSE_DUARTION = 80;
    private static final float SCALE_START_FRACTION = 0.2f;
    private static final float SCALE_END_FRACTION = 0.8f;

    //初始时左小球半径
    private float radius1;
    //初始时右小球半径
    private float radius2;
    //两小球直接的间隔
    private float gap;
    //小球从右边移动到左边时大小倍数变化(rtl = right to left)
    private float rtlScale;
    //小球从左边移动到右边时大小倍数变化
    private float ltrScale;
    //初始左小球颜色
    private Color color1;
    //初始右小球颜色
    private Color color2;
    //两小球重叠处的颜色
    private Color mixColor;
    //小球一次移动时长
    private int duration;
    //小球一次移动后停顿时长
    private int pauseDuration;
    //小球一次移动期间，进度在[0,scaleStartFraction]期间根据rtlScale、ltrScale逐渐缩放，取值为[0,0.5]
    private float scaleStartFraction;
    //小球一次移动期间，进度在[scaleEndFraction,1]期间逐渐恢复初始大小,取值为[0.5,1]
    private float scaleEndFraction;

    //绘图
    private Paint paint1, paint2, mixPaint;
    private Path ltrPath, rtlPath;
    //小球一次移动距离(即两球圆点之间距离）
    private float distance;

    //动画
    private AnimatorValue anim;
    //小球一次移动动画的进度百分比
    private float fraction = 0;
    boolean isAnimCanceled = false;
    //true = 【初始左球】当前正【从左往右】移动,false = 【初始左球】当前正【从右往左】移动
    boolean isLtr = false;

    private BallDancerElement(IComponentCallback iComponentCallback) {
        this.callback = iComponentCallback;
    }

    @Override
    public ArrayList<AnimatorValue> onCreateAnimators() {
        ArrayList<AnimatorValue> list = new ArrayList<>();
        fraction = 0f;
        anim = new AnimatorValue();

        anim.setDuration(duration);
        if (pauseDuration > 0) {
            anim.setDelay(pauseDuration);
            anim.setCurveType(Animator.CurveType.ACCELERATE_DECELERATE);
        } else {
            anim.setLoopedCount(Animator.INFINITE);
            anim.setCurveType(Animator.CurveType.LINEAR);
        }
        anim.setValueUpdateListener((animatorValue, v) -> {
            fraction = v;
            callback.reflushComponent();
        });
        anim.setStateChangedListener(new SimpleAnimatorListener() {
            @Override
            public void onStart(Animator animator) {
                super.onStart(animator);
                isLtr = !isLtr;
            }

            @Override
            public void onCancel(Animator animator) {
                super.onCancel(animator);
                isAnimCanceled = true;
            }

            @Override
            public void onEnd(Animator animator) {
                super.onEnd(animator);
                if (!isAnimCanceled) {
                    callback.getContext().getUITaskDispatcher().delayDispatch(() -> anim.start(), pauseDuration);
                }
            }
        });
        anim.setLoopedListener(animator -> isLtr = !isLtr);
        list.add(anim);
        return list;
    }

    @Override
    public void onAnimationReset() {
        //do nothing because donot need to reset animation params
    }

    @Override
    public BaseAnimationController.IAnimationController<AnimatorValue> initAnimationController() {
        return this;
    }

    @Override
    public void initPaint() {
        super.initPaint();
        paint1 = new Paint();
        paint2 = new Paint();
        mixPaint = new Paint();

        paint1.setAntiAlias(true);
        paint2.setAntiAlias(true);
        mixPaint.setAntiAlias(true);

        paint1.setColor(color1);
        paint2.setColor(color2);
        mixPaint.setColor(mixColor);

        ltrPath = new Path();
        rtlPath = new Path();
    }

    @Override
    public void draw(Canvas canvas) {
        super.draw(canvas);
        float centerY = getHeight() / 2.0f;

        float ltrInitRadius, rtlInitRadius;
        Paint ltrPaint, rtlPaint;

        //确定当前【从左往右】移动的是哪颗小球
        if (isLtr) {
            ltrInitRadius = radius1;
            rtlInitRadius = radius2;
            ltrPaint = paint1;
            rtlPaint = paint2;
        } else {
            ltrInitRadius = radius2;
            rtlInitRadius = radius1;
            ltrPaint = paint2;
            rtlPaint = paint1;
        }


        float ltrX = getWidth() / 2.0f - distance / 2.0f;
        //当前从左往右的球的X坐标
        ltrX = ltrX + (distance * fraction);

        float rtlX = getWidth() / 2.0f + distance / 2.0f;
        //当前从右往左的球的X坐标
        rtlX = rtlX - (distance * fraction);

        //计算小球移动过程中的大小变化
        float ltrBallRadius, rtlBallRadius;
        if (fraction <= scaleStartFraction) {
            //动画进度[0,scaleStartFraction]时，球大小由1倍逐渐缩放至ltrScale/rtlScale倍
            //百分比转换 [0,scaleStartFraction]] -> [0,1]
            float scaleFraction = 1.0f / scaleStartFraction * fraction;
            ltrBallRadius = ltrInitRadius * (1 + (ltrScale - 1) * scaleFraction);
            rtlBallRadius = rtlInitRadius * (1 + (rtlScale - 1) * scaleFraction);
        } else if (fraction >= scaleEndFraction) {
            //动画进度[scaleEndFraction,1]，球大小由ltrScale/rtlScale倍逐渐恢复至1倍
            //百分比转换，[scaleEndFraction,1] -> [1,0]
            float scaleFraction = (fraction - 1) / (scaleEndFraction - 1);
            ltrBallRadius = ltrInitRadius * (1 + (ltrScale - 1) * scaleFraction);
            rtlBallRadius = rtlInitRadius * (1 + (rtlScale - 1) * scaleFraction);
        } else {
            //动画进度[scaleStartFraction,scaleEndFraction]，球保持缩放后的大小
            ltrBallRadius = ltrInitRadius * ltrScale;
            rtlBallRadius = rtlInitRadius * rtlScale;
        }

        ltrPath.reset();
        ltrPath.addCircle(ltrX, centerY, ltrBallRadius, Path.Direction.CLOCK_WISE);
        rtlPath.reset();
        rtlPath.addCircle(rtlX, centerY, rtlBallRadius, Path.Direction.CLOCK_WISE);

        canvas.drawPath(ltrPath, ltrPaint);
        canvas.drawPath(rtlPath, rtlPaint);
    }

    private void apply(BallDancerBuilder builder) {
        RADIUS = DensityUtil.dip2px(callback.getContext(), 6);
        GAP = DensityUtil.dip2px(callback.getContext(), 0.8f);
        radius1 = builder.radius1 > 0 ? builder.radius1 : RADIUS;
        radius2 = builder.radius2 > 0 ? builder.radius2 : RADIUS;
        gap = builder.gap >= 0 ? builder.gap : GAP;
        rtlScale = builder.rtlScale > 0 ? builder.rtlScale : RTL_SCALE;
        ltrScale = builder.ltrScale > 0 ? builder.ltrScale : LTR_SCALR;
        color1 = builder.color1 != null ? builder.color1 : Color.BLUE;
        color2 = builder.color2 != null ? builder.color2 : Color.GREEN;
        mixColor = builder.mixColor != null ? builder.mixColor : Color.BLACK;
        duration = builder.duration > 0 ? builder.duration : DURATION;
        pauseDuration = builder.pauseDuration > 0 ? builder.pauseDuration : PAUSE_DUARTION;
        if (builder.scaleStartFraction <= 0 || builder.scaleStartFraction > valueZeroPointFive) {
            scaleStartFraction = SCALE_START_FRACTION;
        } else {
            scaleStartFraction = builder.scaleStartFraction;
        }
        if (builder.scaleEndFraction <= valueZeroPointFive || builder.scaleEndFraction > 1) {
            scaleEndFraction = SCALE_END_FRACTION;
        } else {
            scaleEndFraction = builder.scaleEndFraction;
        }
        distance = gap + radius1 + radius2;
        initPaint();
    }

    public static class BallDancerBuilder {
        private IComponentCallback iComponentCallback;
        private float radius1;
        private float radius2;
        private float gap;
        private float rtlScale;
        private float ltrScale;
        private Color color1;
        private Color color2;
        private Color mixColor;
        private int duration;
        private int pauseDuration;
        private float scaleStartFraction;
        private float scaleEndFraction;

        public BallDancerBuilder setRadius1(float radius1) {
            this.radius1 = radius1;
            return this;
        }

        public BallDancerBuilder setRadius2(float radius2) {
            this.radius2 = radius2;
            return this;
        }

        public BallDancerBuilder setGap(float gap) {
            this.gap = gap;
            return this;
        }

        public BallDancerBuilder setRtlScale(float rtlScale) {
            this.rtlScale = rtlScale;
            return this;
        }

        public BallDancerBuilder setLtrScale(float ltrScale) {
            this.ltrScale = ltrScale;
            return this;
        }

        public BallDancerBuilder setColor1(Color color1) {
            this.color1 = color1;
            return this;
        }

        public BallDancerBuilder setColor2(Color color2) {
            this.color2 = color2;
            return this;
        }

        public BallDancerBuilder setMixColor(Color mixColor) {
            this.mixColor = mixColor;
            return this;
        }

        public BallDancerBuilder setDuration(int duration) {
            this.duration = duration;
            return this;
        }

        public BallDancerBuilder setPauseDuration(int pauseDuration) {
            this.pauseDuration = pauseDuration;
            return this;
        }

        public BallDancerBuilder setScaleStartFraction(float scaleStartFraction) {
            this.scaleStartFraction = scaleStartFraction;
            return this;
        }

        public BallDancerBuilder setScaleEndFraction(float scaleEndFraction) {
            this.scaleEndFraction = scaleEndFraction;
            return this;
        }

        public BallDancerBuilder(IComponentCallback iComponentCallback) {
            this.iComponentCallback = iComponentCallback;
        }

        public BallDancerElement build() {
            BallDancerElement ballDancerElement = new BallDancerElement(iComponentCallback);
            ballDancerElement.apply(this);
            return ballDancerElement;
        }
    }
}
